/*
 * Project CHEN Rendering Engine : /sppm.h
 * Created: 2021-01-27, Last modified:  2021-02-02
 * 
 * This is the SPPM shader header file.
 * 
 * Copyright (C) 2021 Hineven (hineven@pku.edu.cn)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _H_SHADER_SPPM
#define _H_SHADER_SPPM
#include "../core/shader.hpp"
#include "../core/material.hpp"
#include "../core/optics.hpp"
#include "../core/bsdf.hpp"
#include "../core/rng.hpp"
namespace chen {

struct VisiblePoint ;

class SPPMShader: public Shader {


public:

    Sampler * sampler_f;
    int iters; // Number of iterations 
    Float initial_radius; // Initial radius for Li estimation using photons
    int photons_per_iter; // Number of photons shot for every iteration
    int max_bounce; 
    int samples_per_pixel; // Samples taken for each pixel to generate visible points
    int nlight_samples;
    Float alpha; 

private:
    void traceCameraRays();
    void tracePhotons();
    void gatherResults();

    const int BLOCK_SIZE = 32;

    int _cur_w, _cur_h;
    pthread_mutex_t flock;
    inline void resetFetchNext () {
        _cur_w = _cur_h = 0;
    }
    inline bool fetchNext (int * w, int * h) {
        pthread_mutex_lock(&flock);
        if(_cur_h > camera->film->resolution_h) {
            pthread_mutex_unlock(&flock);
            return false;
        }
        *w = _cur_w, *h = _cur_h;
        _cur_w += BLOCK_SIZE;
        if(_cur_w > camera->film->resolution_w)
            _cur_w = 0, _cur_h += BLOCK_SIZE;
        pthread_mutex_unlock(&flock);
        return true;
    }

    bool traceCameraRay (RayDifferential r, Float pdf, Sampler * sampler, VisiblePoint * vp);
    void traceRandomPhoton (int tid, Sampler * sp);

public:
    
    inline SPPMShader (
        const Scene * sce, const Camera * cam, Sampler * sampler, 
        int iters = 15, Float initial_radius = 2, int photons_per_iter = 100000, 
        int max_bounce = 12, int samples_per_pixel = 1, int nlight_samples = 3, Float alpha = 0.65):
            Shader::Shader(sce, cam), sampler_f(sampler), iters(iters), initial_radius(initial_radius),
            photons_per_iter(photons_per_iter), max_bounce(max_bounce),
            samples_per_pixel(samples_per_pixel), nlight_samples(nlight_samples),
            alpha(alpha) {
                pthread_mutex_init(&flock, nullptr);
            }

    ~SPPMShader () ;

    void prepare (int ntr) ;
    
    void render () ;
    
};

}
#endif